#include "precompiled.h"
#include "optimizegems.h"
using namespace weo::engine::optimization;
using namespace weo::engine::character;
using namespace weo::engine::item;
using namespace std;
size_t OptimizeGems::hashGems(GemPtr gems[s_Gemslotcount])
{
    size_t Hash = 0;
    for(unsigned int j = 0;j<s_Gemslotcount;j++)
    {
        size_t hashval = 0;
        if(gems[j] != NULL) hashval = gems[j]->Hash();
        boost::hash_combine(Hash, hashval);
    }
    return Hash;
}
inline bool OptimizeGems::Valid(GemPtr gems[s_Gemslotcount])
{
    size_t hashval = hashGems(gems);
    bool retval = Optimization::Valid(m_Equip.Items,gems,m_Equip.Enchants);
    if(!retval)
    {
        return false;
    }
    if(m_HashSet.count(hashval) == 0) // not found
    {
        m_HashSet.insert(hashval);
        return true;
    }
    return false;
}
float OptimizeGems::evaluateTF(GemPtr gems[s_Gemslotcount])
{
    m_Character->evaluateEquip(m_Equip.Items,gems,m_Equip.Enchants);
    float fitness = m_Character->evaluateTF();
    return fitness;
}
OptimizeGems::OptimizeGems()
{
}
float OptimizeGems::optimizeGems(weo::engine::item::GemPtr inputgems[s_Gemslotcount], int locked)
{
    bool flags[s_Gemslotcount];
    for(int i = 0;i<s_Gemslotcount;i++)
    {
        if(inputgems[i] != NULL && i != locked) flags[i] = true;
        else flags[i] = false;
    }
    GemPtr gems[s_Gemslotcount];
    GemPtr backup[s_Gemslotcount];
    memcpy(gems,inputgems,sizeof(GemPtr)*s_Gemslotcount);
    float maxfitness = m_Equip.Fitness;
    if(!weo::engine::item::Item::CheckMetaGemRequirements(gems))
    {
        maxfitness = 0;
    }
    float fitness = 0;
    unsigned int gemcount = m_Items[m_Gemindex].size();
    boost::timer full;
    full.restart();
    m_StartLoop.restart();
    while(m_StartLoop.elapsed() < 2)
    {		
        memcpy(backup,gems,sizeof(GemPtr)*s_Gemslotcount);
        for(int i = 0;i<3;i++)
        {
            unsigned int slot = rand() % weo::engine::Slotcount;
            int socketcount = m_Equip.Items[slot]->Sockets()->Count();
            if(socketcount == 0) continue;
            unsigned int gemnr = rand() % socketcount;
            fitness = 0;
            if(flags[slot*3+gemnr])
            {
                const Item* gem = m_Items[m_Gemindex][rand() % gemcount];			
                if(m_Equip.Items[slot]->Sockets()->Colors()->at(gemnr) == 8 && gem->Subtype() != 8) continue;
                else if(m_Equip.Items[slot]->Sockets()->Colors()->at(gemnr) != 8 && gem->Subtype() == 8) continue;
                else
                {
                    gems[slot*3+gemnr] = gem;
                }
            }
        }
        if(Valid(gems))
        {
            fitness = evaluateTF(gems);
        }
        if(fitness > maxfitness)
        {				
            maxfitness = fitness;
            if(full.elapsed() < 60)
            {
                m_StartLoop.restart();
            }
        }
        else
        {
            memcpy(gems,backup,sizeof(GemPtr)*s_Gemslotcount);;
        }
    }
    memcpy(inputgems,gems,sizeof(GemPtr)*s_Gemslotcount);
    return maxfitness;
}
// first optimize generically
// second find best metagem
// fit metagem requirements
void OptimizeGems::Optimize()
{
    std::srand(std::clock());
    GemPtr gems[s_Gemslotcount];
    memcpy(gems,m_Equip.Gems,sizeof(GemPtr)*s_Gemslotcount);
    float maxfitness = optimizeGems(gems);
    const Item *head = m_Equip.Items[0];
    unsigned int metagempos = -1;
    for(unsigned int i = 0;i<head->Sockets()->Count();i++)
    {
        if(head->Sockets()->Colors()->at(i) == 8)
        {
            metagempos = i; 
        }
    }
    if(metagempos != -1)
    {
        const Item* bestmetagem = gems[metagempos];
        for(unsigned int i = 0;i<m_Items[m_Gemindex].size();i++)
        {
            const Item* gem = m_Items[m_Gemindex][i];			
            if(gem->Subtype() != 8) continue;
            gems[metagempos] = gem;
            float fitness = 0;
            fitness = evaluateTF(gems);
            if(fitness > maxfitness)
            {
                maxfitness = fitness;
                bestmetagem = gem;
            }
        }
        gems[metagempos] = bestmetagem;
        maxfitness = optimizeGems(gems,metagempos);
    }
    memcpy(m_Equip.Gems,gems,sizeof(GemPtr)*s_Gemslotcount);
    m_Equip.Fitness = maxfitness;
    m_EndSignal();
}
OptimizeGems::~OptimizeGems()
{
    m_HashSet.clear();
    m_HashSet.resize(0);
}